﻿global using Devonline.Core;
global using Devonline.Entity;
global using static Devonline.Core.AppSettings;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.HttpLogging;
using Microsoft.Net.Http.Headers;

namespace Devonline.AspNetCore;

/// <summary>
/// 基于 AspNetCore Http Web 项目的基础配置项
/// </summary>
public class HttpSetting : AppSetting
{
    /// <summary>
    /// 默认的认证类型, Oidc
    /// </summary>
    public AuthType AuthType { get; set; } = AuthType.Cookie;
    /// <summary>
    /// 启用 Authentication 和 Authorization 功能, 指系统开启了身份认证和授权功能
    /// </summary>
    public bool EnableAuth { get; set; } = true;
    /// <summary>
    /// 是否使用 Http 协议重定向
    /// </summary>
    public bool HttpFordwarded { get; set; } = true;
    /// <summary>
    /// 默认的时间类型是否输出为字符串格式
    /// </summary>
    public bool DateTimeToString { get; set; } = true;
    /// <summary>
    /// 是否启用静态文件
    /// </summary>
    public bool UseStaticFiles { get; set; }
    /// <summary>
    /// 启用 IP 地址认证
    /// 启用此功能将限制可以访问服务器接口的 IP 地址
    /// </summary>
    public bool AuthorizeIpAddress { get; set; }
    /// <summary>
    /// 启用实名认证
    /// </summary>
    public bool EnableRealNameAuth { get; set; }
    /// <summary>
    /// Cookie 的有效期, 决定登录时长, 单位: 分钟, 默认: 480 分钟, 即 8 小时
    /// </summary>
    public int CookieExpireTime { get; set; } = UNIT_EIGHT * UNIT_MINUTES_AN_HOUR;
    /// <summary>
    /// 验证码到期时间, 单位: 秒
    /// </summary>
    public int CaptchaExpireTime { get; set; } = UNIT_SECONDS_A_MINUTE;
    /// <summary>
    /// 版权申明
    /// </summary>
    public string? Copyright { get; set; }
    /// <summary>
    /// 许可证
    /// </summary>
    public string? License { get; set; }
    /// <summary>
    /// 默认的资源文件地址
    /// </summary>
    public string? ResourcesPath { get; set; }
    /// <summary>
    /// 默认密码
    /// </summary>
    public string? DefaultPassword { get; set; }
    /// <summary>
    /// 默认身份数据库连接字符串
    /// </summary>
    public string? IdentityDbContext { get; set; }
    /// <summary>
    /// 内置用户列表
    /// </summary>
    public string BuiltInUsers { get; set; } = USER_MAINTAINERS;
    /// <summary>
    /// 内置角色列表
    /// </summary>
    public string BuiltInRoles { get; set; } = GROUP_MAINTAINERS;
    /// <summary>
    /// 身份组-实名认证用户, 指通过了实名认证, 但是没有进一步操作权限的人
    /// </summary>
    public string IdentityGroupAuthorizedUser { get; set; } = "实名认证用户";
    /// <summary>
    /// SameSiteMode 配置
    /// </summary>
    public Microsoft.AspNetCore.Http.SameSiteMode SameSiteMode { get; set; } = Microsoft.AspNetCore.Http.SameSiteMode.Lax;
    /// <summary>
    /// 是否启用全局拦截
    /// </summary>
    public bool EnableIntercept { get; set; }
    /// <summary>
    /// 自动条件过滤, 默认不启用
    /// </summary>
    public bool AutoFilter { get; set; }
    /// <summary>
    /// 是否允许使用原图
    /// 此开关作用于全局模式, 定义是否允许将服务器保存的图片原图向客户端提供
    /// </summary>
    public bool EnableOriginalImage { get; set; }
    /// <summary>
    /// 自动条件过滤时, 针对字符串类型的字段, 默认使用的过滤操作类型, 默认使用包含关系, 也即: 模糊匹配
    /// </summary>
    public OperatorType DefaultOperatorType { get; set; } = OperatorType.Contains;
    /// <summary>
    /// DCS 服务器配置, 用于向客户端提供 DCS 服务器地址
    /// </summary>
    public string? Dcs { get; set; }

    /// <summary>
    /// 当前节点设置
    /// </summary>
    public DistributedEndpoint Endpoint { get; set; } = new();
    /// <summary>
    /// 附件相关配置项
    /// </summary>
    public AttachmentSetting Attachment { get; set; } = new();
    /// <summary>
    /// HttpLogging 配置
    /// </summary>
    public HttpLoggingSetting? HttpLogging { get; set; }
    /// <summary>
    /// W3C 日志记录器配置
    /// </summary>
    public W3cLoggerSetting? W3CLogger { get; set; }
    /// <summary>
    /// 数据保护配置
    /// </summary>
    public DataProtectionSetting? DataProtection { get; set; }
    /// <summary>
    /// identity options
    /// </summary>
    public IdentitySetting? Identity { get; set; }
    /// <summary>
    /// user interaction setting
    /// </summary>
    public UserInteractionSetting? UserInteraction { get; set; }
    /// <summary>
    /// https certificate configuration
    /// </summary>
    public CertificateSetting? Certificate { get; set; }
    /// <summary>
    /// 图片水印设置, 配置不为空则启用图片水印功能
    /// </summary>
    public ImageWatermarkOptions? Watermark { get; set; }
    /// <summary>
    /// 图片信息设置, 配置不为空则启用图片信息功能
    /// </summary>
    public ImageInformationOptions? ImageInformation { get; set; }
}

/// <summary>
/// 日志相关设置
/// </summary>
public class HttpLoggingSetting
{
    /// <summary>
    /// Gets or sets if the middleware will combine the request, request body, response,
    /// response body, and duration logs into a single log entry. The default is false.
    /// </summary>
    public bool CombineLogs { get; set; } = true;
    /// <summary>
    /// Maximum request body size to log (in bytes). Defaults to 32 KB.
    /// </summary>
    public int RequestBodyLogLimit { get; set; } = UNIT_FOUR * UNIT_EIGHT * UNIT_KILO;
    /// <summary>
    /// Maximum response body size to log (in bytes). Defaults to 32 KB.
    /// </summary>
    public int ResponseBodyLogLimit { get; set; } = UNIT_FOUR * UNIT_EIGHT * UNIT_KILO;
    /// <summary>
    /// HttpLoggingFields 的枚举值
    /// </summary>
    public HttpLoggingFields LoggingFields { get; set; } = HttpLoggingFields.All;
    /// <summary>
    /// 要记录的 Http Request 的 Header 值
    /// Request header values that are allowed to be logged.
    /// If a request header is not present in the Microsoft.AspNetCore.HttpLogging.HttpLoggingOptions.RequestHeaders,
    /// the header name will be logged with a redacted value. Request headers can contain
    /// authentication tokens, or private information which may have regulatory concerns
    /// under GDPR and other laws. Arbitrary request headers should not be logged unless
    /// logs are secure and access controlled and the privacy impact assessed.
    /// </summary>
    public string[] RequestHeaders { get; set; } = [HeaderNames.Cookie, HeaderNames.Authorization, HeaderNames.Origin];
    /// <summary>
    /// 要记录的 Http Response 的 Header 值
    /// Response header values that are allowed to be logged.
    /// If a response header is not present in the Microsoft.AspNetCore.HttpLogging.HttpLoggingOptions.ResponseHeaders,
    /// the header name will be logged with a redacted value.
    /// </summary>
    public string[] ResponseHeaders { get; set; } = [HeaderNames.SetCookie, HeaderNames.Authorization, HeaderNames.Origin, HeaderNames.AccessControlAllowOrigin];
    /// <summary>
    /// Options for configuring encodings for a specific media type.
    /// <para>
    /// If the request or response do not match the supported media type,
    /// the response body will not be logged.
    /// </para>
    /// </summary>
    public string[] MediaTypes { get; set; } = [];

    /// <summary>
    /// 对 HttpLogging 配置进行隐式类型转换
    /// </summary>
    /// <param name="setting"></param>
    public static implicit operator HttpLoggingOptions(HttpLoggingSetting setting)
    {
        var options = new HttpLoggingOptions
        {
            LoggingFields = setting.LoggingFields,
            CombineLogs = setting.CombineLogs,
            RequestBodyLogLimit = setting.RequestBodyLogLimit,
            ResponseBodyLogLimit = setting.ResponseBodyLogLimit
        };

        if (setting.RequestHeaders.Length > 0)
        {
            options.RequestHeaders.AddRangeIfNotContains(setting.RequestHeaders);
        }

        if (setting.ResponseHeaders.Length > 0)
        {
            options.ResponseHeaders.AddRangeIfNotContains(setting.ResponseHeaders);
        }

        if (setting.MediaTypes.Length > 0)
        {
            foreach (var mediaType in setting.MediaTypes)
            {
                options.MediaTypeOptions.AddBinary(mediaType);
            }
        }

        return options;
    }
}

/// <summary>
/// W3C 日志记录器配置
/// </summary>
public class W3cLoggerSetting
{
    /// <summary>
    /// Gets or sets a strictly positive value representing the maximum log size in bytes or null for no limit. Once the log is full, no more messages will be appended.
    /// Defaults to 10MiB.
    /// </summary>
    public int? FileSizeLimit { get; set; }
    /// <summary>
    /// Gets or sets a strictly positive value representing the maximum retained file count. 
    /// Defaults to 4. Must be between 1 and 10,000, inclusive.
    /// </summary>
    public int RetainedFileCountLimit { get; set; } = UNIT_THOUSAND;
    /// <summary>
    /// Gets or sets a string representing the prefix of the file name used to store the logging information.
    /// The current date plus a file number (in the format {YYYYMMDD.X} will be appended to the given value. Defaults to w3clog-.
    /// </summary>
    public string FileName { get; set; } = "w3clog-";
    /// <summary>
    /// Gets or sets a string representing the directory where the log file will be written to.
    /// Defaults to ./logs/ relative to the app directory (ContentRoot). 
    /// If a full path is given, that full path will be used.
    /// If a relative path is given, the full path will be that path relative to ContentRoot.
    /// </summary>
    public string LogDirectory { get; set; } = null!;
    /// <summary>
    /// Gets or sets the period after which logs will be flushed to the store. Defaults to 1 second.
    /// </summary>
    public int FlushInterval { get; set; } = UNIT_TEN;
    /// <summary>
    /// List of additional request header values to log.
    /// Request headers can contain authentication tokens, or private information which may have regulatory concerns under GDPR and other laws.
    /// Arbitrary request headers should not be logged unless logs are secure and access controlled and the privacy impact assessed.
    /// </summary>
    public string[] AdditionalRequestHeaders { get; set; } = [HeaderNames.Origin, HTTP_HEADER_FORWARDED_FOR, HTTP_HEADER_REAL_IP];
    /// <summary>
    /// Fields to log. Defaults to logging request and response properties and headers, plus date/time info and server name.
    /// </summary>
    public W3CLoggingFields LoggingFields { get; set; } = W3CLoggingFields.All;

    /// <summary>
    /// 将 W3cLoggerSetting 转换为 W3CLoggerOptions
    /// </summary>
    /// <param name="setting"></param>
    public static implicit operator W3CLoggerOptions(W3cLoggerSetting setting)
    {
        var w3CLogger = new W3CLoggerOptions
        {
            FileName = setting.FileName,
            FileSizeLimit = setting.FileSizeLimit,
            RetainedFileCountLimit = setting.RetainedFileCountLimit,
            LoggingFields = setting.LoggingFields,
            LogDirectory = setting.LogDirectory,
            FlushInterval = TimeSpan.FromSeconds(setting.FlushInterval)
        };

        if (setting.AdditionalRequestHeaders != null && setting.AdditionalRequestHeaders.Length > 0)
        {
            foreach (var header in setting.AdditionalRequestHeaders)
            {
                w3CLogger.AdditionalRequestHeaders.Add(header);
            }
        }

        return w3CLogger;
    }
}

/// <summary>
/// 数据保护配置
/// </summary>
public class DataProtectionSetting : DataProtectionOptions
{
    public DataProtectionSetting()
    {
        ApplicationDiscriminator = DEFAULT_DATA_PROTECTION_APPLICATION_NAME;
    }

    /// <summary>
    /// 默认数据保护数据库连接字符串
    /// </summary>
    public string? ApplicationDbContext { get; set; }
    /// <summary>
    /// 数据迁移作用的程序集
    /// </summary>
    public string? MigrationsAssembly { get; set; }
}

/// <summary>
/// 认证相关配置选项
/// </summary>
public class IdentitySetting
{
    /// <summary>
    /// 认证服务地址
    /// </summary>
    public string Authority { get; set; } = "https://localhost:9527";
    /// <summary>
    /// api 资源名称
    /// </summary>
    [Column("api_name")]
    public string? ApiName { get; set; }
    /// <summary>
    /// api 资源密钥
    /// </summary>
    [Column("api_secret")]
    public string? ApiSecret { get; set; }
    /// <summary>
    /// 客户端编号
    /// </summary>
    [Column("Client_Id")]
    public string? ClientId { get; set; }
    /// <summary>
    /// 客户端密钥
    /// </summary>
    [Column("client_secret")]
    public string? ClientSecret { get; set; }
    /// <summary>
    /// 授权类型
    /// </summary>
    [Column("grant_type")]
    public string? GrantType { get; set; } = "password";
    /// <summary>
    /// 返回值类型
    /// </summary>
    [Column("response_type")]
    public string? ResponseType { get; set; } = "token";
    /// <summary>
    /// 允许访问的 api 资源范围
    /// </summary>
    public string? Scope { get; set; }
    /// <summary>
    /// 获取 token 的地址
    /// </summary>
    public string Token { get; set; } = "/connect/token";
    /// <summary>
    /// 获取用户信息的地址
    /// </summary>
    public string UserInfo { get; set; } = "/connect/userinfo";
    /// <summary>
    /// Defines whether access and refresh tokens should be stored 
    /// in the Microsoft.AspNetCore.Authentication.AuthenticationProperties after a successful authorization. 
    /// This property is set to false by default to reduce the size of the final authentication cookie.
    /// </summary>
    public bool SaveTokens { get; set; }
    /// <summary>
    /// Gets or sets if HTTPS is required for the metadata address or authority. 
    /// The default is true. This should be disabled only in development environments.
    /// </summary>
    public bool RequireHttpsMetadata { get; set; }
    /// <summary>
    /// Boolean to set whether the handler should go to user info endpoint to retrieve additional claims 
    /// or not after creating an identity from id_token received from token endpoint. The default is 'false'.
    /// </summary>
    public bool GetClaimsFromUserInfoEndpoint { get; set; } = true;
}

/// <summary>
/// 用户交互设置
/// </summary>
public class UserInteractionSetting
{
    /// <summary>
    /// 首页地址
    /// </summary>
    public string Home { get; set; } = "/";
    /// <summary>
    /// 登录地址
    /// </summary>
    public string Login { get; set; } = "/Account/Login";
    /// <summary>
    /// 注销地址
    /// </summary>
    public string Logout { get; set; } = "/Account/Logout";
    /// <summary>
    /// 微信授权地址
    /// </summary>
    public string WeixinAuthorize { get; set; } = "/api/Account/WeixinAuthorize";
    /// <summary>
    /// 微信登录地址
    /// </summary>
    public string WeixinLogin { get; set; } = "/api/Account/WeixinLogin";
    /// <summary>
    /// 外部登录回调地址
    /// </summary>
    public string Callback { get; set; } = "/External/Callback";
    /// <summary>
    /// 用户主页地址
    /// </summary>
    public string Profile { get; set; } = "/Account/Profile";
    /// <summary>
    /// 拒绝访问地址
    /// </summary>
    public string AccessDenied { get; set; } = "/Account/AccessDenied";
    /// <summary>
    /// 错误页面地址
    /// </summary>
    public string Error { get; set; } = "/Home/Error";
    /// <summary>
    /// 身份选择页面地址
    /// </summary>
    public string IdentitySelect { get; set; } = "/Account/IdentitySelect";
    /// <summary>
    /// 用户信息获取地址
    /// </summary>
    public string UserInfo { get; set; } = "/api/Account/GetUserInfo";
    /// <summary>
    /// 用户权限校验地址
    /// </summary>
    public string UserPermission { get; set; } = "/api/Account/UserHasPermission";
    /// <summary>
    /// 给当前用户自动添加角色
    /// </summary>
    public string AutoRole { get; set; } = "/api/Account/AutoRole";
    /// <summary>
    /// 外部登录回调结束后绑定手机号码的地址
    /// </summary>
    public string BindPhoneNumber { get; set; } = "/Account/BindPhoneNumber";
    /// <summary>
    /// 业务系统授权访问地址
    /// 已开启授权访问的低级别用户在登录/访问系统资源时, 会跳转到授权地址, 先得到授权后, 才可以访问
    /// </summary>
    public string AuthorizedAccess { get; set; } = "/Account/AuthorizedAccess";
    /// <summary>
    /// ReturnUrlParameter 确定查询字符串参数的名称，该参数由处理程序在质询期间附加。这也是当请求到达登录路径或注销路径时寻找的查询字符串参数，以便在执行操作后返回到原始url。
    /// </summary>
    public string ReturnUrl { get; set; } = HTTP_REQUEST_DEFAULT_RETURN_URL_PARAMETER;
}

/// <summary>
/// https certificate configuration
/// </summary>
public class CertificateSetting
{
    /// <summary>
    /// 域名
    /// </summary>
    public string? Domain { get; set; }
    /// <summary>
    /// 证书地址
    /// </summary>
    public string? Path { get; set; }
    /// <summary>
    /// 证书密码
    /// </summary>
    public string? Password { get; set; }
}

/// <summary>
/// 附件相关选项
/// </summary>
public class AttachmentSetting
{
    public AttachmentSetting()
    {
        Upload = new()
        {
            Enable = true,
            Extensions = DEFAULT_ALLOW_FILE_EXTENSIONS
        };

        Download = new()
        {
            Enable = true,
            Extensions = DEFAULT_ALLOW_FILE_EXTENSIONS
        };

        Thumbnail = new()
        {
            Enable = true,
            Size = DEFAULT_IMAGE_THUMBNAIL_SIZE,
            Prefix = DEFAULT_IMAGE_THUMBNAIL_PREFIX,
            Extensions = DEFAULT_IMAGE_FILE_EXTENSIONS
        };

        Crop = new()
        {
            Enable = false,
            Size = DEFAULT_IMAGE_CROP_SIZE,
            Extensions = DEFAULT_IMAGE_FILE_EXTENSIONS
        };

        Video = new()
        {
            Enable = false,
            Size = DEFAULT_IMAGE_CROP_SIZE,
            Length = DEFAULT_TOTAL_FILE_SIZE,
            Total = UNIT_GIGA,
            Prefix = DEFAULT_VIDEO_CAPTURE_PREFIX,
            Extensions = DEFAULT_VIDEO_FILE_EXTENSIONS
        };
    }

    /// <summary>
    /// 附件根路径
    /// </summary>
    public string RootPath { get; set; } = DEFAULT_ATTACHMENT_PATH;

    /// <summary>
    /// 文件上传限制
    /// </summary>
    public FileSetting Upload { get; set; }
    /// <summary>
    /// 文件下载限制
    /// </summary>
    public FileSetting Download { get; set; }
    /// <summary>
    /// 图片缩略图
    /// </summary>
    public FileSetting Thumbnail { get; set; }
    /// <summary>
    /// 图片裁剪图
    /// </summary>
    public FileSetting Crop { get; set; }
    /// <summary>
    /// 视频附件
    /// </summary>
    public FileSetting Video { get; set; }
}

/// <summary>
/// 文件相关设置
/// </summary>
public class FileSetting
{
    /// <summary>
    /// 启用文件设置
    /// </summary>
    public bool Enable { get; set; } = false;
    /// <summary>
    /// 图片或者视频的最大边长
    /// </summary>
    public int Size { get; set; } = DEFAULT_IMAGE_THUMBNAIL_SIZE;
    /// <summary>
    /// 单个文件大小限制
    /// </summary>
    public int Length { get; set; } = DEFAULT_MAX_FILE_SIZE;
    /// <summary>
    /// 全部/批量文件大小限制
    /// </summary>
    public int Total { get; set; } = DEFAULT_TOTAL_FILE_SIZE;
    /// <summary>
    /// 进行操作时使用的文件前缀
    /// </summary>
    public string Prefix { get; set; } = DEFAULT_IMAGE_THUMBNAIL_PREFIX;
    /// <summary>
    /// 允许使用的文件类型, 文件扩展名
    /// </summary>
    public string Extensions { get; set; } = DEFAULT_IMAGE_FILE_EXTENSIONS;
}